#pragma once

#include <boost/shared_ptr.hpp>
#include <QVector>
#include <QObject>
#include <QPair>
#include <QMap>

namespace framework {

  class PresenterFactory;

  //
  // framework::IPresenterCreator
  //

  class IPresenterCreator {
  public:
    typedef boost::shared_ptr<IPresenterCreator> SP;

  public:
    virtual ~IPresenterCreator() {}

    virtual void Reg(PresenterFactory *) = 0;
    virtual QObject * Attach(QObject *pModel_, QObject *pView_) = 0;
  };

  //
  // framework::SimplePresenterCreator
  //

  template<class MODEL, class VIEW, class PRESENTER>
  class SimplePresenterCreator : public virtual IPresenterCreator
  {
  public:
    typedef MODEL     TModel;
    typedef VIEW      TView;
    typedef PRESENTER TPresenter;

  public:
    virtual void Reg(PresenterFactory * pFactory_)
    {
    }

    virtual QObject * Attach(QObject *pAbstractModel_, QObject *pAbstractView_)
    {
      TModel * pModel = qobject_cast<TModel *>(pAbstractModel_);
      TView  * pView  = qobject_cast<TView  *>(pAbstractView_);

      if (pModel == 0 || pView == 0)
        return 0;
        
      TPresenter * pPresenter = new TPresenter(pModel, pView);

      pPresenter->setProperty("mvp::view",  qVariantFromValue((QObject *)pView));
      pPresenter->setProperty("mvp::model", qVariantFromValue((QObject *)pModel));

      pView->setProperty("mvp::presenter", qVariantFromValue((QObject *)pPresenter));
      pView->setProperty("mvp::model",     qVariantFromValue((QObject *)pModel));

      QObject::connect(pModel, SIGNAL(destroyed()), pPresenter, SLOT(deleteLater()));
      QObject::connect(pView,  SIGNAL(destroyed()), pPresenter, SLOT(deleteLater()));

      return pPresenter;
    }
  };

  //
  // framework::PresenterFactory
  //

  class PresenterFactory {
  public:

    // Registration
    void Reg(IPresenterCreator::SP pCreator_)
    {
      mCreators.append(pCreator_);
      pCreator_->Reg(this);
    }

    template<class MODEL, class VIEW, class PRESENTER> void Reg()
    {
      Reg(IPresenterCreator::SP(new SimplePresenterCreator<MODEL, VIEW, PRESENTER>()));
    }

    // Construction
    QObject *Make(QObject *pModel_, QObject *pView_) const
    {
      QObject *pPresenter = 0;

      foreach (IPresenterCreator::SP pCreator, mCreators)
      {
        pPresenter = pCreator->Attach(pModel_, pView_);

        if (pPresenter != 0)
          break;
      }

      return pPresenter;
    }

  private:
    typedef QList<IPresenterCreator::SP> TCreatorContainer;
    TCreatorContainer mCreators;
  };
} // namespace mvp {